#include <stdio.h>
#include <stdlib.h>
#include <string>
#include "ICHAT_PacketBase.h"
using namespace std;

extern "C"  
{  
    #include "lua.h"  
    #include "lauxlib.h"  
    #include "lualib.h"  
}  

#if defined(_WIN32) || defined(_WIN64)
#define _DLLExport _declspec(dllexport)
#else
#define _DLLExport 
#endif

static int lua_create_writebase(lua_State* L)
{
    NETOutputPacket** p = (NETOutputPacket**)lua_newuserdata(L, sizeof(NETInputPacket));
    *p = new NETOutputPacket;
    
    luaL_getmetatable(L, "packetbase");
    lua_setmetatable(L, -2);

    return 1;
}

static int lua_create_readbase(lua_State* L)
{
    NETInputPacket** p = (NETInputPacket**)lua_newuserdata(L, sizeof(NETInputPacket));
    *p = new NETInputPacket;

    luaL_getmetatable(L, "packetbase");
    lua_setmetatable(L, -2);

    return 1;
}

static int WriteBegin(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    short nCommand = luaL_checkinteger(L, 2);
   
    //默认值
    char cVersion = 3;
    char cSubVersion = 2;
    
    int ver_flag = lua_isinteger(L, 3);
    if (ver_flag==1) {
        cVersion = luaL_checkinteger(L, 3);
    }
    int sub_ver_flag = lua_isinteger(L, 4);
    if (sub_ver_flag==1) {
        cSubVersion = luaL_checkinteger(L, 4);
    }
  
    //printf("cVersion=%d\n", cVersion);
    //printf("cSubVersion=%d\n", cSubVersion);

    (*p)->Begin(nCommand, cVersion, cSubVersion);

    return 1;
}

static int WriteEnd(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    (*p)->End();
    (*p)->EncryptBuffer();

    return 1;
}

static int EncryptBuffer(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    (*p)->EncryptBuffer();

    return 1;
}

static int DecryptBuffer(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    int encrypt_size = (*p)->DecryptBuffer();
    lua_pushinteger(L, encrypt_size);

    return 1;
}

static int Copy(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    void* msg = lua_touserdata(L, 2);
    luaL_argcheck(L, msg != NULL, 1, "invalid msg data");

    int len = (int)luaL_checkinteger(L, 3);

    int encrypt_size = (*p)->Copy(msg, len);

    lua_pushinteger(L, encrypt_size);

    return 1;
}

static int GetCmdType(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    short nCmdType = (*p)->GetCmdType();
    lua_pushinteger(L, nCmdType);

    return 1;
}

static int return_buf(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    int size = (*p)->packet_size();
    char *v = (*p)->return_buf();

    lua_pushlightuserdata(L, v);
    lua_pushinteger(L, size);
    //free(v);
    
    return 2;
}

static int WriteInt(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    int nValue = (int)luaL_checkinteger(L, -1);
    bool ret = (*p)->WriteInt(nValue);

    lua_pushinteger(L, ret);

    return 1;
}

static int WriteString(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");
    
    const char *pString = luaL_checkstring(L, -1);
    
    bool ret = (*p)->WriteString(pString);

    lua_pushinteger(L, ret);

    return 1;
}

static int WriteShort(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    short nValue = (short)luaL_checkinteger(L, -1);
    bool ret = (*p)->WriteShort(nValue);

    lua_pushinteger(L, ret);

    return 1;
}

static int WriteInt64(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    long long nValue = luaL_checknumber(L, -1);

    bool ret = (*p)->WriteInt64(nValue);

    lua_pushinteger(L, ret);

    return 1;
}

static int WriteFloat64(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    double nValue = luaL_checknumber(L, -1);

    bool ret = (*p)->WriteFloat64(nValue);

    lua_pushinteger(L, ret);

    return 1;
}

static int WriteByte(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    unsigned char nValue = luaL_checkinteger(L, -1);
    bool ret = (*p)->WriteByte(nValue);

    lua_pushinteger(L, ret);

    return 1;
}

static int ReadInt(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    int v = (*p)->ReadInt();

    lua_pushinteger(L, v);

    return 1;
}

static int ReadShort(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    short v = (*p)->ReadShort();

    lua_pushinteger(L, v);

    return 1;
}

static int ReadByte(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    unsigned char t = (*p)->ReadByte();
    //const char* v = (const char*)&t;

    lua_pushinteger(L, t);

    return 1;
}

static int ReadInt64(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    long long v = (*p)->ReadInt64();

    //string s = (char*)((*p)->ReadInt64());
    //lua_pushstring(L, s.c_str());

    lua_pushinteger(L, v);
    
    return 1;
}

static int ReadString(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    string v = (*p)->ReadString();
    lua_pushstring(L, v.c_str());

    return 1;
}

static int lua_delete_base(lua_State* L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "packetbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");
    
    if (p) {
        //std::cout<<"lua_delete_base!"<<endl;
        delete *p;
    }

    return 1;
}

static const luaL_Reg registerInfo[] = {
    {"__gc", lua_delete_base},
   
    {"create_writebase", lua_create_writebase},
    {"create_readbase", lua_create_readbase},
    {"read_int", ReadInt},
    {"read_short", ReadShort},
    {"read_byte", ReadByte},
    {"read_int64", ReadInt64},
    {"read_string", ReadString},

    {"write_begin", WriteBegin},
    {"write_end", WriteEnd},
    {"write_int", WriteInt},
    {"write_int64", WriteInt64},
    {"write_float64", WriteFloat64},
    {"write_byte", WriteByte},
    {"write_string", WriteString},
    {"write_short", WriteShort},

    //{"encrypt_buffer", EncryptBuffer},
    {"return_buf", return_buf},

    {"decrypt_buffer", DecryptBuffer},
    {"copy", Copy},
    {"get_cmd_type", GetCmdType},

    {NULL, NULL}
};


extern "C" _DLLExport int luaopen_lua_packetbase(lua_State *L)
{
    luaL_newmetatable(L, "packetbase");
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");

	luaL_setfuncs(L, registerInfo, 0);

    return 1;
}
